/**
 * 
 */
package merisis.impl;

import java.util.HashMap;
import java.util.logging.Logger;

import merisis.util.BitSet;
import merisis.util.BitSetIterator;
import merisis.util.BitUtil;
import merisis.util.OpenBitSetImpl;

import com.google.inject.Inject;
import com.google.inject.Injector;

public class SiloDisk {

	private boolean confused = false;

	final HashMap<Long, BitSet> ontosMap = new HashMap<Long, BitSet>();

	final HashMap<Long, BitSet> portsMap = new HashMap<Long, BitSet>();

	BitSet vertexes = new OpenBitSetImpl();

	private BitSet frozen = new OpenBitSetImpl();

	private SiloDisk previous;

	private int depthnum = 0;

	private long spindle = 0;

	private Silo silo;

	@Inject
	private Hippocampus hippocampus;

	@Inject
	private Logger logger;

	@Inject
	private Injector injector;

	private Siloyard yard;

	public SiloDisk(Siloyard yard, SiloDisk previous, Silo silo) {

		this.previous = previous;
		this.silo = silo;
		this.spindle = silo.getTopVertex();
		this.yard = yard;
	}

	public SiloDisk getPrevious() {

		return previous;
	}

	public BitSet getVertexes() {

		if (confused)
			nap();

		return vertexes;
	}

	public boolean contains(long vertex) {
		return false;
	}

	public BitSet getAntecedents(long vertex) {

		nap();

		return ontosMap.get(vertex);
	}

	public boolean isConfused() {

		return confused;
	}

	public void add(long v) {

		vertexes.set(v);

		if (!confused)
			confused = true;
	}

	public void add(BitSet vs) {

		vertexes.or(vs);

		if (!confused)
			confused = true;
	}

	public void nap() {

		if (!confused)
			return;

		collectFromHippocampus();

		pile();

		confused = false;
	}

	private void collectFromHippocampus() {

		BitSetIterator itr = vertexes.iterator();
		while (itr.hasNext()) {

			long v = itr.next();

			BitSet ontos;
			{

				long[] ontoarray = hippocampus.getAntecedent(v);
				if (ontoarray == null)
					continue;

				ontos = BitUtil.toBitSet(ontoarray);
			}

			BitSet delivered = new OpenBitSetImpl();
			for (long key = ontos.nextSetBit(0); key >= 0; key = ontos
					.nextSetBit(key + 1)) {

				if (key == spindle) {

					delivered.set(key);
					continue;
				}

				if (vertexes.get(key)) {

					delivered.set(key);
					continue;
				}

				if (previous != null && previous.vertexes != null
						&& previous.vertexes.get(key)) {

					delivered.set(key);
					continue;
				}
			}

			if (delivered.isEmpty())
				continue;

			if (delivered.cardinality() < ontos.cardinality()) {

				if (silo.addedSPorts == null)
					silo.addedSPorts = new OpenBitSetImpl();

				silo.addedSPorts.set(v);
				portsMap.put(v, new OpenBitSetImpl());
			}

			// check the frozen vertices
			BitSet ports = yard.portsMap.get(v);
			if (ports != null) {
				frozen.set(v);

				for (BitSetIterator rItr = ports.iterator(); rItr.hasNext();) {

					Silo silo = yard.rootMap.get(rItr.next());
					silo.connectPort(v, ontos, spindle);
				}
			}

			hippocampus.transit(spindle, v, delivered);
			ontosMap.put(v, ontos);
		}
	}

	private void pile() {

		BitSet pushed = new OpenBitSetImpl();

		BitSetIterator itr = vertexes.iterator();
		while (itr.hasNext()) {

			long v = itr.next();
			if (frozen.get(v))
				continue; // the frozen vertices are always on the surface.

			if (pushed.get(v))
				continue;

			BitSet ontos = ontosMap.get(v);
			if (ontos != null && vertexes.countIntersectionWith(ontos) > 0)
				pushed.or(ontos);

			// // union the ontos of v from hippocampus and disk.
			// {
			// BitSet vs1;
			// vs1 = ontosMap.get(v);
			// long[] vs2;
			// // vs2 = hippocampus.getAntecedent(v);
			// if (vs1 != null && vs2 != null) {
			// ontos = vs1.clone();
			// ontos.or(BitUtil.toBitSet(vs2));
			// } else if (vs2 != null) {
			// ontos = BitUtil.toBitSet(vs2);
			// } else if (vs1 != null) {
			// ontos = vs1;
			// } else
			// continue;
			// }
		}

		if (pushed.isEmpty())
			return;

		pushed.and(vertexes);

		if (previous == null) {
			previous = new SiloDisk(yard, null, silo);
			injector.injectMembers(previous);
		} else {
			previous.depthnum++;
		}

		for (long v = pushed.nextSetBit(0); v >= 0; v = pushed
				.nextSetBit(v + 1)) {

			previous.add(v);

			previous.ontosMap.put(v, ontosMap.remove(v));
		}

		vertexes.andNot(pushed);

		confused = false;
	}

	private void pack() {

	}

	public void dumpLayerInfo(StringBuilder sb) {

		if (confused)
			sb.append("!");
		sb.append("(");

		BitSetIterator itr = vertexes.iterator();
		if (itr.hasNext()) {

			long vertex = itr.next();
			if (frozen.get(vertex))
				sb.append("_");
			sb.append(vertex);
			sb.append("<");

			BitSet ontos = ontosMap.get(vertex);
			if (ontos == null)
				sb.append("null");
			else
				sb.append(ontos.toString());

			while (itr.hasNext()) {

				vertex = itr.next();
				sb.append(',');
				sb.append(vertex);
				sb.append("|");
				ontos = ontosMap.get(vertex);
				sb.append(ontos.toString());
			}
		}
		sb.append(")");
	}
}